﻿/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "WorldSocketDelegate.h"
#include <cassert>
#include <NodeCpp/Platform.h>
#include <NodeCpp/AutoPtr.h>
#include <NodeCpp/Random.h>
#include <NodeCpp/ByteConverter.h>
#include "Shared/Log/Log.h"
#include "Shared/WorldPacket.h"
#include "Shared/PacketHeader.h"
#include "AuthSessionManager.h"
#include "AuthSession.h"
#include "WorldSession.h"
using NodeCpp::AutoPtr;
using namespace NodeCpp;

WorldSocketDelegate::WorldSocketDelegate(void)
    : AuthSession_(NULL)
    , WorldSession_(NULL)
    , PacketProcessing_(false)
    , Closing_(false)

    , Authed_(false)

    , Seed_(sRandom.Random32())
    , AuthCrypt_()
    , SendLock_()

    , ClientHeaderRecvSize_(0)
    , ClientPacketRecvSize_(0)
    , ClientPacket_(NULL)

    , LastPingTime_(0)
    , OverSpeedPings_(0)
{

}

WorldSocketDelegate::~WorldSocketDelegate(void)
{

}

void WorldSocketDelegate::OnOpen(void)
{
    /* 打开连接时创建验证会话 */
    if (!(AuthSession_ = sAuthSessionMgr.NewSession(this))) {
        Closing_ = true;
        peer().Close();
        return;
    }

    /* 开始验证 */
    AuthSession_->SendAuthChallenge(Seed_);

    sLog.outDebug("Connection(%d) OPENED from %s:%d",
        peer().id(), peer().host().c_str(), peer().port());
}

void WorldSocketDelegate::OnRead(void)
{
    if (Closing_) {
        return;
    }

    PacketProcessing_ = true;

    //sLog.outDebug("Connection(%d) RECEIVED(%d) bytes",
    //    peer().id(), peer().GetReceivedSize());

    /******************** 接收数据包 ********************/
    bool _Error = false;
    std::size_t _ReceivedSize = peer().GetReceivedSize();

    while (_ReceivedSize > 0)
    {
        if (ClientPacket_ == NULL) {
            /* 接收包头 */
            std::uint16_t _HeadSpace = sizeof(ClientPktHeader) - ClientHeaderRecvSize_;
            int _BytesRecv = peer().Read(&ClientHeader_[ClientHeaderRecvSize_], _HeadSpace);
            if (_BytesRecv < 0) {
                break;
            }
            ClientHeaderRecvSize_ += (std::uint16_t)_BytesRecv;
            _ReceivedSize -= (std::size_t)_BytesRecv;

            if (ClientHeaderRecvSize_ < sizeof(ClientPktHeader)) {
                break;
            }

            /* 包头接收完毕，处理包头数据 */
            if (!_HandleInputHeader()) {
                break;
            }
        }

        if (!ClientPacket_) {
            sLog.outDebug("forcing close on input client_packet_ = NULL");
            _Error = true;
            break;
        }

        /* 接收包体 */
        std::uint16_t _PacketSpace = std::uint16_t(ClientPacket_->size() - ClientPacketRecvSize_);
        if (_PacketSpace > 0)
        {
            int _BytesRecv = peer().Read(ClientPacket_->contents(ClientPacketRecvSize_), _PacketSpace);
            if (_BytesRecv < 0) {
                break;
            }
            ClientPacketRecvSize_ += (std::uint16_t)_BytesRecv;
            _ReceivedSize -= (std::size_t)_BytesRecv;

            if (ClientPacketRecvSize_ < ClientPacket_->size()) {
                break;
            }
        }

        /* 数据包接收完毕 */
        if (!_HandleInputPayload()) {
            _Error = true;
            break;
        }
    }

    PacketProcessing_ = false;

    if (_Error) {
        Closing_ = true;
    }
}

void WorldSocketDelegate::OnWrite(void)
{

}

void WorldSocketDelegate::OnEof(void)
{
    sLog.outDebug("Connection(%d) EOF", peer().id());

    Closing_ = true;
}

void WorldSocketDelegate::OnClose(void)
{
    sLog.outDebug("Connection(%d) CLOSED", peer().id());
}

void WorldSocketDelegate::OnError(void)
{
    sLog.outDebug("Connection(%d) ERROR, %d, %s",
        peer().id(), peer().GetErrorCode(), peer().GetErrorString().c_str());

    Closing_ = true;
}

void WorldSocketDelegate::OnTimeout(int)
{

}

bool WorldSocketDelegate::TryClose(void)
{
    Closing_ = true;

    if (!PacketProcessing_) {
        peer().Close();
        return true;
    }
    return false;
}

void WorldSocketDelegate::SendPacket(WorldPacket& _Pkt)
{
    NODECPP_LOCK_MUTEX(_Guard, SendLock_);

    /* 加密包头 */
    struct ServerPktHeader _Header;
    _Header.Cmd = _Pkt.opcode();
    _Header.Size = (std::uint16_t)_Pkt.size() + 2;
    ByteConverter::EndianConvertReverse(_Header.Size);
    ByteConverter::EndianConvert(_Header.Cmd);
    AuthCrypt_.EncryptSend((std::uint8_t*)&_Header, sizeof(_Header));

    peer().Send((const std::uint8_t*)&_Header, sizeof(_Header));
    peer().Send(_Pkt.contents(), _Pkt.size());
}

bool WorldSocketDelegate::_HandleInputHeader(void)
{
    NODECPP_ASSERT(ClientPacket_ == NULL);
    NODECPP_ASSERT(ClientHeaderRecvSize_ == sizeof(ClientPktHeader));

    /* 解密包头 */
    AuthCrypt_.DecryptRecv(ClientHeader_, sizeof(ClientPktHeader));
    struct ClientPktHeader &_Header = *((struct ClientPktHeader*)ClientHeader_);
    ByteConverter::EndianConvertReverse(_Header.Size);
    ByteConverter::EndianConvert(_Header.Cmd);

    if ((_Header.Size < 4) || (_Header.Size > 10240) || (_Header.Cmd > 10240)) {
        sLog.outError("client sent malformed packet size = %d , cmd = %d", _Header.Size, _Header.Cmd);
        return false;
    }

    /* 创建数据包接收包体 */
    _Header.Size -= 4;
    ClientPacket_ = new WorldPacket(_Header.Cmd, _Header.Size);
    if (!ClientPacket_) {
        sLog.outError("failed malloc WorldPacket");
        return false;
    }

    if (_Header.Size > 0) {
        ClientPacket_->Resize(_Header.Size, true);
        ClientPacketRecvSize_ = 0;
    }
    else {
        NODECPP_ASSERT(ClientPacket_->size() == 0);
    }
    return true;
}

bool WorldSocketDelegate::_HandleInputPayload(void)
{
    NODECPP_ASSERT(ClientPacket_ != NULL);
    NODECPP_ASSERT(ClientPacketRecvSize_ == ClientPacket_->size());

    bool _Ok = true;

    if (!Authed_) {
        /* 连接未通过验证时所有数据包交由验证会话处理 */
        AuthSession_->QueuePacket(ClientPacket_);
    } else {
        /* 连接通过验证后由网络层处理（CMSG_PING，CMSG_KEEP_ALIVE）消息，其他交给游戏会话处理 */
        switch(ClientPacket_->opcode()) {
        case CMSG_PING:
            sLog.outDebug("(%d) Received CMSG_PING", peer().id());
            _Ok = _HandlePing(ClientPacket_);
            break;
        case CMSG_KEEP_ALIVE:
            sLog.outDebug("(%d) Received CMSG_KEEP_ALIVE", peer().id());
            break;
        default:
            sLog.outDebug("(%d) Received opcode(%d)", peer().id(), ClientPacket_->opcode());
            WorldSession_->QueuePacket(ClientPacket_);
        }
    }

    /* 重置数据，准备接收下一个包 */
    ClientPacket_ = NULL;
    ClientPacketRecvSize_ = 0;
    ClientHeaderRecvSize_ = 0;
    return _Ok;
}

bool WorldSocketDelegate::_HandlePing(WorldPacket* _Pkt)
{
    AutoPtr<WorldPacket> _Packet(_Pkt);

    std::uint32_t _Ping;
    std::uint32_t _Latency;
    (*_Packet) >> _Ping;
    (*_Packet) >> _Latency;

    if (LastPingTime_ == 0) {
        ::time(&LastPingTime_);
    } 
    else
    {
        time_t _CurTime; time(&_CurTime);
        time_t _DiffTime = _CurTime - LastPingTime_;
        LastPingTime_ = _CurTime;

        if (_DiffTime < 27) {
        }
        else {
            OverSpeedPings_ = 0;
        }
    }

    {
        if (WorldSession_)
        {
            WorldSession_->SetLatency(_Latency);
            WorldSession_->SetClientDelay(0);
        }
        else
        {
            sLog.outError("peer sent CMSG_PING, "
                "but is not authenticated or got recently kicked,"
                " address = %s",
                peer().host().c_str());
        }
    }

    WorldPacket _Resp(SMSG_PONG, 4);
    _Resp << _Ping;
    SendPacket(_Resp);
    return true;
}